package run;

import static log.LogConstants.*;
import static run.scenario.ScenarGenerator.ValeursImages;
import static run.scenario.ScenarGenerator.ValeursSides;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Calendar;
import java.util.HashMap;

import javax.swing.Timer;

import log.LogManager;
import run.graphics.ExpeScreen;
import run.graphics.StartDialog;
import run.scenario.GeneratorConstants;
import run.scenario.Scenario;


/**
 * 
 * @author Mathieu Nancel
 *
 */
public class ScenarioManager {

	////////////////////////////////////////////////////////////////////////////
	
//	/**
//	 * Time after the images have been displayed, in ms
//	 */
//	public static final int TIME_ANSWER = 200;
//	
//	public static final int TRIAL_TIME = TIME_ANSWER + TIME_DISPLAY + TIME_INIT;
	
	////////////////////////////////////////////////////////////////////////////	
	
	public enum STATE {
		BLANK, IMAGES, ANSWER
	}
	
	private STATE currentState = STATE.BLANK;
	
	public enum SESSION {
		GERMAN, ENGLISH
	}
	
	////////////////////////////////////////////////////////////////////////////
	
	public static ScenarioManager INSTANCE;
	
	protected Scenario scenario;
	// protected DashBoard dashboard;
	protected ExpeScreen expeScreen;
	
	protected boolean canStart = false;
	
	// protected Recorder recorder;
	
	protected int participant;
	protected SESSION session;
	protected int currentTrial;
	
	protected int nbAnswers;
	
	protected LogManager logManager;

	protected boolean quiting = false;
	
	////////////////////////////////////////////////////////////////////////////
	
	protected int timeInit = -1;
	protected int timeDisplay = -1;
	protected int imageSize = -1;
	protected int arrowSize = -1;
	protected int imageDistance = -1;
	protected boolean vertical = true;
	protected boolean circle = true;
	protected boolean dunnoButton = false;
	
	////////////////////////////////////////////////////////////////////////////
	
	protected Timer initTimer;
	protected Timer displayTimer;
	protected Timer answerTimer;
	protected Timer currentTimer;
	
	////////////////////////////////////////////////////////////////////////////
	
	protected long startTrialTime;
	protected long startSpeechTime;
	protected long startAnswerTime;
	protected long firstPressTime;
	protected long endTrialTime;
	
	////////////////////////////////////////////////////////////////////////////
	
	protected StartDialog dialog;
	
	////////////////////////////////////////////////////////////////////////////
	
	protected HashMap<String, String[]> language = new HashMap<String, String[]>();
	
	////////////////////////////////////////////////////////////////////////////
	
	/**
	 * 
	 */
	public ScenarioManager(String fileName) {
		
		try {
			BufferedReader labelFileReader = new BufferedReader(new FileReader("config_files"+File.separator+"Language.txt"));
			
			String line = "";
			while( (line = labelFileReader.readLine()) != null) {
				
				if (line.startsWith("//")) {
					continue;
				}
				String[] words = line.split("=");
				language.put(words[0], words[1].split(":"));
				
			}
			
			labelFileReader.close();
			
			//////////////////////////
			
			BufferedReader configFileReader = new BufferedReader(new FileReader("config_files"+File.separator+"parameters.txt"));
			
			line = "";
			double angle1 = -1, angle2 = -1;
			int distance = -1;
			
			while( (line = configFileReader.readLine()) != null) {
				
				if (line.startsWith("//")) {
					continue;
				}
				String[] words = line.split(": ");
				
				if (words[0].contains("Time before images")) {
					timeInit = Integer.parseInt(words[1]);
				}
				
				if (words[0].contains("Images display time")) {
					timeDisplay = Integer.parseInt(words[1]);
				}
				
				if (words[0].contains("Closest angle")) {
					angle1 = Double.parseDouble(words[1]);
				}
				
				if (words[0].contains("Farthest angle")) {
					angle2 = Double.parseDouble(words[1]);
				}
				
				if (words[0].contains("Distance to screen")) {
					distance = Integer.parseInt(words[1]);
				}
				
				if (words[0].contains("Arrow size")) {
					arrowSize = Integer.parseInt(words[1]);
				}
				
				if (words[0].contains("Buttons layout")) {
					vertical = words[1].equalsIgnoreCase("vertical");
					circle = words[1].equalsIgnoreCase("circle");
				}
				
				if (words[0].contains("Dunno button")) {
					dunnoButton = words[1].equalsIgnoreCase("true");
				}
				
			}
			
			double pt1 = 8.9 * distance * Math.tan(Math.PI*angle1/180d);
			double pt2 = 8.9 * distance * Math.tan(Math.PI*angle2/180d);
			
			imageSize = (int)(pt2 - pt1);
			imageDistance = (int)(pt1 - arrowSize / 2d);
			
			System.out.println(imageSize + ", " + imageDistance);
			
			configFileReader.close();
			
			System.out.println("Init time: " + timeInit + "ms");
			System.out.println("Display time: " + timeDisplay + "ms");
			System.out.println("Image size: " + imageSize + "ms");
			System.out.println("Arrow size: " + arrowSize + "ms");
			System.out.println("Distance between arrow and images: " + imageDistance + "ms");
			System.out.println("Buttons Layout: " + (vertical?"vertical":"horizontal") );
			
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		
		/*
		 * Creating the Scenario
		 */
		
		scenario = new Scenario(fileName);
		// dashboard = new DashBoard(this);
		
		// recorder = new Recorder(this);
		
		// dashboard.createTrialList(Integer.parseInt(scenario.getConstant(NbTrials)));
		
		/*
		 * Timers
		 */
		
		// Initial 500 ms 
		
		initTimer = new Timer(timeInit, new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent evt) {
				expeScreen.displayTrial();
				currentTimer = displayTimer;
				currentState = STATE.IMAGES;
				currentTimer.start();
			}
		});
		
		// Display the images
		
		displayTimer = new Timer(timeDisplay, new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// expeScreen.displayLines();
				// currentTimer = answerTimer;
				//currentState = STATE.LINES;
				// currentTimer.start();
				
				expeScreen.displayButtons();
				currentState = STATE.ANSWER;
				
				startAnswerTime = System.currentTimeMillis();
			}
		});
		
//		answerTimer = new Timer(TIME_ANSWER, new ActionListener() {
//			@Override
//			public void actionPerformed(ActionEvent e) {
//				expeScreen.emptyScreen();
//				
//				if (!scenario.goOn()) {
//					quit();
//				}
//				currentTrial = Integer.parseInt(scenario.getFactorValue(Trial));
//				initTrial();
//				
//				currentTimer = initTimer;
//				currentState = STATE.BLANK;
//				currentTimer.start();
//			}
//		});
		
		initTimer.setRepeats(false);
		displayTimer.setRepeats(false);
//		answerTimer.setRepeats(false);
		
		System.out.println( "Experiment name : " + scenario.getConstant(GeneratorConstants.ExpName) );
		
		dialog = new StartDialog(this, expeScreen);
		
		INSTANCE = this;
		
		expeScreen = new ExpeScreen(this, imageSize, arrowSize, imageDistance, vertical, circle, dunnoButton);
		expeScreen.emptyScreen();
		
	}
	
	public void initTrial() {
		nbAnswers = 0;
		firstPressTime = -1;
		
		int newLeft = Integer.parseInt(scenario.getFactorValue(LeftImage));
		int newRight = Integer.parseInt(scenario.getFactorValue(RightImage));
		int newSide = Integer.parseInt(scenario.getFactorValue(Reading));
		expeScreen.updateTrialData(newLeft, newRight, newSide);
		
		// dashboard.setTrialNb(Integer.parseInt(scenario.getFactorValue(Trial)));
		
		startTrialTime = System.currentTimeMillis();
	}
	
	public void startTrials() {
		
		if (canStart) {
			
			scenario.start(participant);
			if (currentTrial != 1) {
				scenario.goTo(Trial, ""+(currentTrial));
			}
			initTrial();
			// recorder.start();
			initTimer.start();
			
		} else {
			System.err.println("Problem; study can't start");
		}
		
	}
	
	public void validate(String answer) {
		System.out.println("> "+ answer);
		
		logManager.logValue(Knew, "true");
		logManager.logValue(Answer, answer);
		
		answerGiven();
	}
	
	public void dunno() {
		if (firstPressTime == -1) {
			firstPressTime = System.currentTimeMillis();
		}
		
		System.out.println("Dunno");
		
		logManager.logValue(Knew, "false");
		logManager.logValue(Answer, "");
		
		answerGiven();
	}
	
	private void answerGiven() {
		endTrialTime = System.currentTimeMillis();
		
		log();
		expeScreen.emptyScreen();
		
		if (!scenario.goOn()) {
			quit();
		}
		currentTrial = Integer.parseInt(scenario.getFactorValue(Trial));
		initTrial();
		
		currentTimer = initTimer;
		currentState = STATE.BLANK;
		currentTimer.start();
	}
	
	public void pauseExpe() {
		
		
		
	}
	
	public void log() {
		
		logFactor(logManager, Trial);
		
		logManager.logValue(Participant, ""+participant); // meh...
		
		String left = ValeursImages[Integer.parseInt(scenario.getFactorValue(LeftImage))];
		String right = ValeursImages[Integer.parseInt(scenario.getFactorValue(RightImage))];
		String reading = ValeursSides[Integer.parseInt(scenario.getFactorValue(Reading))];
		
		logManager.logValue(Session, session.name());
		
		logManager.logValue(LeftImage, left);
		logManager.logValue(RightImage, right);
		logManager.logValue(Reading, reading);
		logManager.logValue(TargetImage, reading.equalsIgnoreCase("left")?left:right);
		
		logManager.logValue(Error, ""+!logManager.getCurrentValue(Answer).equals(logManager.getCurrentValue(TargetImage)));
		
		logManager.logValue(NbAnswers, ""+nbAnswers);
		logManager.logValue(TimeStart, ""+startTrialTime);
		logManager.logValue(TimeAnswerStart, ""+startAnswerTime);
		logManager.logValue(TimeFirstPress, ""+ firstPressTime);
		logManager.logValue(TimeEnd, ""+endTrialTime);
		
		logManager.logValue(AnswerTime, ""+(endTrialTime-startAnswerTime));
		logManager.logValue(ReactionTime, ""+(firstPressTime-startAnswerTime));
		
		// TODO mp3 file url
		// TODO save file
		
		// Light log, can be flushed immediately
		logManager.writeLogLine(true, true);
	}
			
	public void initLogs() {
		
		/*
		 * Creating the log managers
		 */
		
		Calendar calendar = Calendar.getInstance();
		String date = "" +
			(calendar.get(Calendar.DAY_OF_MONTH) < 10 ? "0" : "") + calendar.get(Calendar.DAY_OF_MONTH) + "-" + 
			(calendar.get(Calendar.MONTH) < 9 ? "0" : "") + (calendar.get(Calendar.MONTH) + 1) + "-" + 
			calendar.get(Calendar.YEAR) + "_" + 
			(calendar.get(Calendar.HOUR_OF_DAY) < 10 ? "0" : "") + calendar.get(Calendar.HOUR_OF_DAY) + "h" +
			(calendar.get(Calendar.MINUTE) < 10 ? "0" : "") + calendar.get(Calendar.MINUTE);
		
		PrintStream out;
		try {
			out = new PrintStream(new FileOutputStream(
					"logs" + File.separatorChar + "errors" + File.separatorChar + 
					"errors_Participant-" + participant + "_"+ session + "_" + date + ".txt"
			));
			System.setErr(out);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
		logManager = new LogManager("Trials", "logs" + File.separatorChar + "logs" + File.separatorChar + 
				"Participant-" + participant + "_" + session + "_" + date + ".csv", true, true,
				
				// Scenario
				
				Participant,
				Trial,
				Session,
				
				// Factors
				
				LeftImage,
				RightImage,
				Reading,
				TargetImage,
				
				// Measures
				
				Answer,
				Knew,
				Error,
				NbAnswers,
				
				ReactionTime,
				AnswerTime,
				
				TimeStart,
				TimeEnd,
				TimeAnswerStart,
				TimeFirstPress
		);
		
//		cineLogManager = new ChunkLogger(
//				"Cinematics",
//				"logs" + File.separatorChar + "cinelogs" + File.separatorChar + "cinelogs_P" + participantId + "_" + date + ".csv", 
//				false, 
//				cinelog,
//				CINE_LOG_FREQUENCY,
//				
//				// Scenario
//				
//				Participant,
//				Block,
//				Trial,
//				Target,
//				
//				// Factors
//				
//				Technique,
//				Distance,
//				Amplitude,
//				AmplitudeValue,
//				Width,
//				WidthValue,
//				Training,
//				ConfigName,
//				QuantizedCoarse,
//				UsesHead,
//				
//				TargetXMm,
//				TargetYMm,
//				
//				ZoneWidth,
//				ZoneHeight,
//				FingerX,
//				FingerY,
//				FingerSpeed,
//				
//				// Measures
//				
//				When,
//				Phase,
//				Mode,
//				Pressed,
//				
//				OnTarget,
//				Clutch,
//				
//				CursorX,
//				CursorY,
//				
//				HatGazeXMm,
//				HatGazeYMm,
//				HatVelocityMm,
//				HatDistanceMm
//				
//		) {
//			
//			protected Point2D.Double previousDeviceAngle = new Point2D.Double();
//			protected Point2D.Double previousHeadAngle = new Point2D.Double();
//			protected Point2D.Double previousHeadCoords = new Point2D.Double();
//			
//			protected double deviceDist, headDistRad, headDistMm;
//			
//			@Override
//			public void atLog() {
//				// cineLog(); // XXX
//				/*
//				cineLogManager.logValue(HatDistanceMm, ""+(lastHeadCoords.getX() - startHeadCoords.getX()));
//				
//				double dir = (targetId == 0) ? 1d : -1d;
//				
//				deviceDist = dir * ( previousDeviceAngle.getX() - lastDeviceAngle.getX() );
//				headDistRad = dir * ( previousHeadAngle.getX() - lastHeadAngle.getX() );
//				headDistMm = dir * ( previousHeadCoords.getX() - lastHeadCoords.getX() );
//				
//				cineLogManager.logValue(HatVelocityMm, ""+headDistMm);
//				
//				previousDeviceAngle.setLocation(lastDeviceAngle);
//				previousHeadAngle.setLocation(lastHeadAngle);
//				previousHeadCoords.setLocation(lastHeadCoords);
//				
//				// gM.addValuesToCurve(lastDeviceAngle.x, lastHeadAngle.x, isPressed);
//				*/
//			}
//			
//			@Override
//			public boolean filter() {
//				
//				boolean result = !previousLine.containsKey(When)	// Previous line has no timestamp -> automatic yes
//						|| (
//							/*
//							 * Timestamps must be different.
//							 * We only keep the earliest log (can't really  do otherwise).
//							 */ 
//							previousLine != null &&
//							logs.containsKey(When) &&
//							logs.get(When) != null &&
//							!logs.get(When).equalsIgnoreCase(previousLine.get(When))
//						)
//				;
//				
//				if (!result) {
//					
//					if (previousLine == null) {
//						System.err.println("previousLine is null");
//					} else if (! logs.containsKey(When)) {
//						System.err.println("No key \"" + When + "\" in the logs");
//					} else if (logs.get(When) == null) {
//						// System.err.println("Null value associated to key " + When); // XXX décommenter
//					} else if (logs.get(When).equalsIgnoreCase(previousLine.get(When))) {
//						System.err.println("Same timestamp as previous line");
//					}
//					
//				}
//				
//				return result;
//				
//			}
//			
//		};
		
		logManager.setLog(true);
//		cineLogManager.setLog(cinelog);
		
		
		/*
		 * 
		 */
		
	}
	
	public void incrementNbAnswers() {
		if (firstPressTime == -1) {
			firstPressTime = System.currentTimeMillis();
		}
		
		nbAnswers++;
	}
	
	public void quit() {
		
		quiting = true;
			
		System.out.println("Start flushing log.");
		
		logManager.flush();
		logManager.close();
		
		System.out.println("Log written. Start flushing cinematics.");
		
//		cineLogManager.stop();
//		cineLogManager.flush();
//		cineLogManager.close();
//		
//		System.out.println("Cinematics written.");
        
		System.exit(0);
		
	}
	
	protected void logFactor(LogManager logManager, String factorName) {
		logManager.logValue(factorName, scenario.getFactorValue(factorName));
	}
	
	public STATE getCurrentState() {
		return currentState;
	}
	
	public void setParticipant(int p) {
		participant = p;
	}
	
	public void setSession(int s) {
		session = SESSION.values()[s];
	}
	
	public void setCurrentTrial(int currentTrial) {
		this.currentTrial = currentTrial;
	}
	
	public void setCanStart(boolean canStart) {
		this.canStart = canStart;
	}
	
	public String getWord(String label) {
		return language.get(label)[session.ordinal()];
	}
	
//	public DashBoard getDashboard() {
//		return dashboard;
//	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		ScenarioManager manager = new ScenarioManager("experiment_voice.txt");
		
	}
	
}
